<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>Custom Scopes and Attributes - Rauthy Documentation</title>


        <!-- Custom HTML head -->

        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff">

        <link rel="icon" href="../favicon.svg">
        <link rel="shortcut icon" href="../favicon.png">
        <link rel="stylesheet" href="../css/variables.css">
        <link rel="stylesheet" href="../css/general.css">
        <link rel="stylesheet" href="../css/chrome.css">
        <link rel="stylesheet" href="../css/print.css" media="print">

        <!-- Fonts -->
        <link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" id="highlight-css" href="../highlight.css">
        <link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
        <link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">

        <!-- Custom theme stylesheets -->
        <link rel="stylesheet" href=".././mdbook-admonish.css">


        <!-- Provide site root and default themes to javascript -->
        <script>
            const path_to_root = "../";
            const default_light_theme = "light";
            const default_dark_theme = "navy";
        </script>
        <!-- Start loading toc.js asap -->
        <script src="../toc.js"></script>
    </head>
    <body>
    <div id="mdbook-help-container">
        <div id="mdbook-help-popup">
            <h2 class="mdbook-help-title">Keyboard shortcuts</h2>
            <div>
                <p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
                <p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
                <p>Press <kbd>?</kbd> to show this help</p>
                <p>Press <kbd>Esc</kbd> to hide this help</p>
            </div>
        </div>
    </div>
    <div id="body-container">
        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script>
            try {
                let theme = localStorage.getItem('mdbook-theme');
                let sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script>
            const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
            let theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            const html = document.documentElement;
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add("js");
        </script>

        <input type="checkbox" id="sidebar-toggle-anchor" class="hidden">

        <!-- Hide / unhide sidebar before it is displayed -->
        <script>
            let sidebar = null;
            const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            } else {
                sidebar = 'hidden';
            }
            sidebar_toggle.checked = sidebar === 'visible';
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <!-- populated by js -->
            <mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
            <noscript>
                <iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
            </noscript>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle">
                <div class="sidebar-resize-indicator"></div>
            </div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky">
                    <div class="left-buttons">
                        <label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </label>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Rauthy Documentation</h1>

                    <div class="right-buttons">
                        <a href="../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script>
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="custom-scopes-and-attributes"><a class="header" href="#custom-scopes-and-attributes">Custom Scopes and Attributes</a></h1>
<p>OpenID Connect defines quite a few very useful claims to be included in the <code>id_token</code> as additional information about
a user. However, there are times when you need other data as well, and maybe it is data that multiple downstream
applications need, like for instance an internal employee ID.</p>
<p>Rauthy can handle this as well. There are custom scopes you can create. These do not get mixed up with the default
scopes, so they can't interfere, which makes the whole setup pretty easy.</p>
<div id="admonition-info" class="admonition admonish-info" role="note" aria-labelledby="admonition-info-title">
<div class="admonition-title">
<div id="admonition-info-title">
<p>Info</p>
</div>
<a class="admonition-anchor-link" href="#admonition-info"></a>
</div>
<div>
<p>Users are not allowed to self-manage their custom scope values.<br />
This must be done by an admin.</p>
</div>
</div>
<p>Values are mapped as claims into tokens only if allowed an requested for an individual client. This means even if you
set a value for a user, it will not just end up in the token automatically. Let's walk through how you can achieve that.</p>
<h2 id="custom-user-attribute"><a class="header" href="#custom-user-attribute">Custom User Attribute</a></h2>
<p>The first thing we will do is to create a custom user attribute. Rauthy does not allow you to just enter anything you
like. All values are strictly typed and must be pre-configured to prevent user error during normal operation. For
instance, you won't be able to do typos, don't need copy &amp; paste from other entries, and so on.</p>
<p>In the Admin UI, navigate to <code>Attributes</code> -&gt; <code>Add New User Attribute</code></p>
<p><img src="img/cust_attr.png" alt="custom attribute" /></p>
<p>The description is optional and can be changed at any time without any impact. The <code>name</code> of the attribute will be the
exact same in the JWT token later on.</p>
<h2 id="set-user-values"><a class="header" href="#set-user-values">Set User Values</a></h2>
<p>Now that we created our custom attribute in the step above, we can set them for users. Navigate to a user of your
choice, expand the entry and choose the <code>Attributed</code> tab.</p>
<p><img src="img/set_user_attr.png" alt="set user attribute" /></p>
<div id="admonition-info-1" class="admonition admonish-info" role="note" aria-labelledby="admonition-info-1-title">
<div class="admonition-title">
<div id="admonition-info-1-title">
<p>Info</p>
</div>
<a class="admonition-anchor-link" href="#admonition-info-1"></a>
</div>
<div>
<p>The custom values for users are always interpreted, saved and returned <code>String</code>s without any further validation.</p>
</div>
</div>
<h2 id="create-a-custom-scope"><a class="header" href="#create-a-custom-scope">Create a Custom Scope</a></h2>
<p>The next thing we need is a custom scope we can map attributes to. You are not allowed to modify the OIDC default
scopes, but you can add your own ones, as many as you like. This is the first step we need to do. Navigate to the</p>
<p>Admin UI -&gt; <code>Scopes</code> -&gt; <code>Add New Scopes</code></p>
<p><img src="img/cust_scope.png" alt="custom scope" /></p>
<p>After saving, expand your just created scope. You want to map your attribute from the step before into JWT tokens
when a client requests (and is allowed to) this scope. For instance, let's add our new attribute to the <code>id_token</code>
with this custom scope.</p>
<p><img src="img/cust_scope_mapping.png" alt="custom scope mapping" /></p>
<p>We are almost done.</p>
<h2 id="allow-scope-for-client"><a class="header" href="#allow-scope-for-client">Allow Scope for Client</a></h2>
<p>The very last step is one you probably know. A client needs to be allowed to request certain scopes in the first place.
From this point on, the new custom scope behaves like any default one. We need to allow it for a client to be requested.</p>
<p>Open the configuration for any <code>Client</code> and scroll down until you see the <code>Allowed Scopes</code> and <code>Default Scopes</code> section.</p>
<p><img src="img/client_scope_allow.png" alt="allow client scope request" /></p>
<p>The <code>Allowed Scopes</code> are the ones a client is allowed to request when redirecting a user to the authorization endpoint
to initiate the <code>authorization_code</code> flow. The <code>Default Scopes</code> are the ones that Rauthy will simply always add. This is
useful for instance when you are using <code>client_credentials</code> or another flow, because only the <code>authorization_code</code>
flow can request specific scopes while all others can't.</p>
<h2 id="finish"><a class="header" href="#finish">Finish</a></h2>
<p>That is it. It seems like many steps to follow, but it is to your advantage. With this setup you have type-safety and
the most flexibility about which client can see which values in which context, and so on.</p>
<p>When you have done everything right, you will see your new custom value in the <code>custom</code> section of the JWT token claims.
I used the <code>rauthy</code> client in this example (which you should not mess with when deployed).</p>
<pre><code class="language-json">{
  "iat": 1721722389,
  "exp": 1721722399,
  "nbf": 1721722389,
  "iss": "http://localhost:8080/auth/v1",
  "sub": "za9UxpH7XVxqrtpEbThoqvn2",
  "aud": "rauthy",
  "nonce": "Fars0wPqrm9f6XimDKD08CPl",
  "azp": "rauthy",
  "typ": "Id",
  "amr": [
    "pwd"
  ],
  "auth_time": 1721722389,
  "at_hash": "Hd5ugcSzxwl1epF7Il3pEpq0gznnqs2SnSVZCdNw0EI",
  "preferred_username": "admin@localhost",
  "roles": [
    "rauthy_admin",
    "admin"
  ],
  "custom": {
    "my_attr": "This is Batman!"
  }
}
</code></pre>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../work/api_keys.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>

                            <a rel="next prefetch" href="../work/ephemeral_clients.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>

                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../work/api_keys.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>

                    <a rel="next prefetch" href="../work/ephemeral_clients.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>




        <script>
            window.playground_copyable = true;
        </script>


        <script src="../elasticlunr.min.js"></script>
        <script src="../mark.min.js"></script>
        <script src="../searcher.js"></script>

        <script src="../clipboard.min.js"></script>
        <script src="../highlight.js"></script>
        <script src="../book.js"></script>

        <!-- Custom JS scripts -->


    </div>
    </body>
</html>
